﻿using Loki.Bot;
using Loki.Game;
using Loki.Game.GameData;
using Loki.Game.Objects;
using Loki.TreeSharp;
using Loki.Utilities;
using log4net;
using Action = Loki.TreeSharp.Action;

namespace toNyxIncinerate
{
    // ReSharper disable InconsistentNaming
    public class toNyxIncinerate : CombatRoutine
    // ReSharper restore InconsistentNaming
    {

        #region CombatRoutine Implementations

        private PrioritySelector BuffComposite { get; set; }
        private PrioritySelector CombatComposite { get; set; }

        private bool _eventsHooked;

        /// <summary> Gets the name. </summary>
        /// <value> The name. </value>
        public override string Name { get { return "toNyxIncinerate"; } }

        /// <summary> Gets the buff behavior. </summary>
        /// <value> The buff composite. </value>
        public override Composite Buff { get { return BuffComposite; } }

        /// <summary> Gets the combat behavior. </summary>
        /// <value> The combat composite. </value>
        public override Composite Combat { get { return CombatComposite; } }

        /// <summary>
        /// On CR Config Button press
        /// </summary>
        public override void OnGuiButtonPress()
        {
            var mySettingsForm = new CrSettingsForm();
            mySettingsForm.Show();
        }

        /// <summary>
        ///     Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <filterpriority>2</filterpriority>
        public override void Dispose() { }

        /// <summary>
        ///     Performs weaponswap
        /// </summary>
        public void SwapWeapons() { LokiPoe.Input.PressKey(LokiPoe.ConfigManager.GetActionKey(ActionKey.WeaponSwap).Item1); }

        /// <summary> Initializes this <see cref="CombatRoutine" />. </summary>
        public override void Initialize()
        {
            if (_eventsHooked) return;

            // When we start the bot, we need to re-register all the available spells. This allows us to swap out skills
            // Without restarting the bot.

            BotMain.OnStart += bot => DoSpellRegistrations();
            _eventsHooked = true;
        }

        private void DoSpellRegistrations()
        {
            //BotMain.TicksPerSecond = AbilitySettings.Instance.TicksPerSeconds;

            CombatComposite = new PrioritySelector(context => Target.BestTarget);
            BuffComposite = new PrioritySelector(context => Target.BestTarget);

            // If the BestTarget is null, we should not do anything.
            CombatComposite.AddChild(
                new Decorator(ret => Target.BestTarget == null,
                    new Action(ret => RunStatus.Success))
                );

            // Ensure we add the flask logic *first*
            // We don't want to be trying to do buffs and whatnot, if we have to pop a flask.
            //BuffComposite.AddChild(CreateFlaskLogic()); REMOVED FOR TEST PURPOSE (PLUGIN)

            // huehue
            CombatComposite.AddChild(Composites.CreateMoveIntoRange());

            // Support for the proximity shield mobs or Out of LoS Mobs
            CombatComposite.AddChild(
                new Decorator(ret => 
                    Target.BestTarget.HasAura("proximity_shield_aura"),

                    Composites.CreateMoveIntoRange(10)));

            Log.Debug("Registering Buffs");
            RegisterBuffs();

            Log.Debug("Registering Curses");
            RegisterCurses();

            Log.Debug("Registering main abilities.");
            RegisterMainAbilities();
        }

        #endregion

        private new static readonly ILog Log = Logger.GetLoggerInstanceForType();
        public static bool IsMeleeBased = false;

        #region Cached Values

        private int? _cachedMaxFrenzyCharges;
        private int? _cachedMaxPowerCharges;

        public int MaxPowerCharges
        {
            get
            {
                if (_cachedMaxPowerCharges != null) return _cachedMaxPowerCharges.Value;
                _cachedMaxPowerCharges = LokiPoe.ObjectManager.Me.GetStat(StatType.MaxPowerCharges);
                return _cachedMaxPowerCharges.Value;
            }
        }

        public int MaxFrenzyCharges
        {
            get
            {
                if (_cachedMaxFrenzyCharges != null) return _cachedMaxFrenzyCharges.Value;
                _cachedMaxFrenzyCharges = LokiPoe.ObjectManager.Me.GetStat(StatType.MaxFrenzyCharges);
                return _cachedMaxFrenzyCharges.Value;
            }
        }

        #endregion

        #region SpellRegistration

            #region Register Spell

            private void Register(string spell)
            {
                Register(spell, ret => true);
            }

            private void Register(string spell, SpellManager.GetSelection<bool> requirement)
            {
                Register(spell, requirement, ret => Target.BestTarget);
            }

            private void Register(string spell, SpellManager.GetSelection<bool> requirement, SpellManager.GetSelection<NetworkObject> on)
            {
                // If we don't have the spell, or it's a totem, ignore it
                if (!SpellManager.HasSpell(spell, true) || SpellManager.GetSpell(spell).GetStat(StatType.IsTotem) != 0)
                    return;

                Log.Debug("• Registered " + spell);
                CombatComposite.AddChild(SpellManager.CreateSpellCastComposite(spell, requirement, on));
            }

            /*
                    private void Register(string spell, SpellManager.GetSelection<bool> requirement, SpellManager.GetSelection<Vector2i> on)
                    {
                        // If we don't have the spell, or it's a totem, ignore it
                        if (!SpellManager.HasSpell(spell, true) || SpellManager.GetSpell(spell).GetStat(StatType.IsTotem) != 0)
                        {
                            return;
                        }

                        Log.Debug("• Registered " + spell);
                        CombatComposite.AddChild(SpellManager.CreateSpellCastComposite(spell, requirement, on));
                    }
            */

            #endregion

            #region Register Buff

            private void RegisterBuff(string spell, SpellManager.GetSelection<bool> requirement)
            {
                // If we don't have the spell, or it's a totem, ignore it
                if (!SpellManager.HasSpell(spell, true) || SpellManager.GetSpell(spell).GetStat(StatType.IsTotem) != 0)
                {
                    return;
                }

                Log.Debug("• Registered Buff " + spell);
                BuffComposite.AddChild(SpellManager.CreateSpellCastNoTargetOrLocationComposite(spell,
                    ret =>
                    {
                        bool req = requirement == null || requirement(ret);
                        if (req)
                        {
                            return !LokiPoe.ObjectManager.Me.HasAura(spell);
                        }

                        return false;
                    }));
            }

            #endregion

            #region RegisterCurse

            private void RegisterCurse(string spell, string curse)
            {
                // 3+ mobs near the best target.
                RegisterCurse(spell, curse, ret => Target.BestTarget.IsCursable && (Target.BestTarget.Rarity >= Rarity.Rare || Utilities.NumberOfMobsNear(Target.BestTarget, 20, AbilitySettings.Instance.ProjectileWeaknessMinimumEnemies)));
            }

            private void RegisterCurse(string spell, string curse, SpellManager.GetSelection<bool> requirement)
            {
                RegisterCurse(spell, curse, requirement, ret => Target.BestTarget);
            }

            private void RegisterCurse(string spell, string curse, SpellManager.GetSelection<bool> requirement, SpellManager.GetSelection<NetworkObject> on)
            {
                // If we don't have the spell, or it's a totem, ignore it
                if (!SpellManager.HasSpell(spell, true) || SpellManager.GetSpell(spell).GetStat(StatType.IsTotem) != 0)
                    return;

                Log.Debug("• Registered Curse " + spell);
                // Note: we're putting this in the buff composite, since we want it to run before the normal combat stuff.
                BuffComposite.AddChild(SpellManager.CreateSpellCastComposite(spell,
                    ret =>
                    {
                        // First, check if there are requirements for this curse.
                        bool req = requirement == null || requirement(ret);

                        // If the requirements have been met, ensure the monster doesn't have the aura we're looking for.
                        if (req)
                            return !((Actor) @on(ret)).HasAura(curse);

                        return false;
                    },
                    on));
            }

            #endregion

        #endregion

        private void RegisterBuffs()
        {
            if (AbilitySettings.Instance.UseArcticArmour)
                RegisterBuff("Arctic Armour", ret => !LokiPoe.ObjectManager.Me.HasAura("ice_shield"));
        }

        private void RegisterCurses()
        {
            if (AbilitySettings.Instance.UseProjectileWeakness)
                RegisterCurse("Projectile Weakness", "curse_projectile_weakness");
        }

        private void RegisterMainAbilities()
        {

            // Testing Vaal Skills
            if (Utilities.HasGemSocketed("Vaal Power Siphon"))
                Register("Vaal Power Siphon", ret =>
                    Utilities.NumberOfMobsNear(LokiPoe.ObjectManager.Me, 20, 6));

            // Core attack
            if (AbilitySettings.Instance.UseFireStorm && Utilities.HasGemSocketed("Firestorm"))
                Register("Firestorm", ret =>
                    Target.BestTarget.Distance > (AbilitySettings.Instance.CombatRange + 8)); // Use FireStorm when out of Incinerate range (leveling purpose)
            else
                Register("Incinerate", ret =>
                    Target.BestTarget.Distance < 40);

            // Default attack(s) (oom or out of incinerate range shit)
            Register("Default Attack");
        }
    }
}
